package vip.liux.backend.application.serviceImpl.pay;


import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import vip.liux.application.dots.PagedResultDto;
import vip.liux.application.dots.PagedResultRequestDto;
import vip.liux.backend.application.service.pay.PayDemoWithdrawService;
import vip.liux.backend.application.service.pay.PayTransferService;
import vip.liux.backend.application.service.pay.dto.demo.PayDemoWithdrawCreateDto;
import vip.liux.backend.application.service.pay.dto.transfer.PayTransferCreateDto;
import vip.liux.backend.application.service.pay.dto.transfer.PayTransferDto;
import vip.liux.contracts.models.pay.PayDemoWithdraw;
import vip.liux.contracts.repositories.pay.PayDemoWithdrawRepository;
import vip.liux.contracts.shared.enums.pay.PayChannelCode;
import vip.liux.contracts.shared.enums.pay.PayDemoWithdrawStatus;
import vip.liux.contracts.shared.enums.pay.PayDemoWithdrawType;
import vip.liux.contracts.shared.enums.pay.PayTransferStatus;
import vip.liux.core.utils.JsonUtils;

import javax.validation.Valid;
import java.util.Objects;

import static vip.liux.application.PageKit.toPage;
import static vip.liux.application.PageKit.toPaged;
import static vip.liux.backend.infrastructure.utils.ServletUtil.getClientIP;
import static vip.liux.contracts.shared.enums.ErrorCode.*;
import static vip.liux.contracts.shared.util.ServiceExceptionUtil.exception;

/**
 * 示例转账业务 Service 实现类
 *
 * @author jason
 */
@Service
@Validated
@Slf4j
public class PayDemoWithdrawServiceImpl implements PayDemoWithdrawService {

    /**
     * 接入的支付应用标识
     * <p>
     * 从 [支付管理 -> 应用信息] 里添加
     */
    private static final String PAY_APP_KEY = "demo";

    @Resource
    private PayDemoWithdrawRepository demoTransferRepo;

    @Resource
    private PayTransferService payTransferApi;

    @Override
    public PayDemoWithdraw createDemoWithdraw(@Valid PayDemoWithdrawCreateDto input) {
        PayDemoWithdraw withdraw = new PayDemoWithdraw()
                .setSubject(input.getSubject())
                .setPrice(input.getPrice())
                .setUserAccount(input.getUserAccount())
                .setUserName(input.getUserName())
                .setType(input.getType())
                .setTransferChannelCode(getTransferChannelCode(input.getType()))
                .setStatus(PayDemoWithdrawStatus.WAITING);
        demoTransferRepo.save(withdraw);
        return withdraw;
    }

    @Override
    public String transferDemoWithdraw(Long id) {
        // 1.1 校验提现单
        PayDemoWithdraw withdraw = validateDemoWithdrawCanTransfer(id);
        // 1.2 特殊：如果是转账失败的情况，需要充值下
        if (PayDemoWithdrawStatus.isClosed(withdraw.getStatus())) {
            int updateCount = demoTransferRepo.updateByIdAndStatus(
                    withdraw.getId(),
                    withdraw.getStatus(),
                    PayDemoWithdrawStatus.WAITING,
                    ""
            );
            if (updateCount == 0) {
                throw exception(DEMO_WITHDRAW_TRANSFER_FAIL_STATUS_NOT_WAITING_OR_CLOSED);
            }
            withdraw.setStatus(PayDemoWithdrawStatus.WAITING);
        }

        // 2.1 创建支付单
        PayTransferCreateDto transferCreateDto = new PayTransferCreateDto()
                .setAppKey(PAY_APP_KEY)
                .setChannelCode(withdraw.getTransferChannelCode()).setUserIp(getClientIP()) // 支付应用
                .setMerchantTransferId(String.valueOf(withdraw.getId())) // 业务的订单编号
                .setSubject(withdraw.getSubject()).setPrice(withdraw.getPrice()) // 价格信息
                .setUserAccount(withdraw.getUserAccount()).setUserName(withdraw.getUserName()); // 收款信息
        if (Objects.equals(withdraw.getType(), PayDemoWithdrawType.WECHAT)) {
            // 注意：微信很特殊！提现需要写明用途！！！
            transferCreateDto.setChannelExtras(PayTransferCreateDto.buildWeiXinChannelExtra1000("测试活动", "测试奖励"));
        }
        PayTransferDto input = payTransferApi.createTransfer(transferCreateDto);

        // 2.2 更新转账单到 demo 示例提现单，并将状态更新为转账中
        demoTransferRepo.updateByIdAndStatus(
                withdraw.getId(),
                withdraw.getStatus(),
                input.getTransferNo()
        );
        return input.getTransferNo();
    }

    private PayDemoWithdraw validateDemoWithdrawCanTransfer(Long id) {
        // 校验存在
        PayDemoWithdraw withdraw = demoTransferRepo.findById(id).orElseThrow(() -> exception(DEMO_WITHDRAW_NOT_FOUND));
        // 校验状态，只有等待中或转账失败的订单，才能发起转账
        if (!PayDemoWithdrawStatus.isWaiting(withdraw.getStatus())
                && !PayDemoWithdrawStatus.isClosed(withdraw.getStatus())) {
            throw exception(DEMO_WITHDRAW_TRANSFER_FAIL_STATUS_NOT_WAITING_OR_CLOSED);
        }
        return withdraw;
    }

    private String getTransferChannelCode(PayDemoWithdrawType type) {
        if (Objects.equals(type, PayDemoWithdrawType.ALIPAY)) {
            return PayChannelCode.ALIPAY_PC.name();
        }
        if (Objects.equals(type, PayDemoWithdrawType.WECHAT)) {
            return PayChannelCode.WX_LITE.name();
        }
        if (Objects.equals(type, PayDemoWithdrawType.WALLET)) {
            return PayChannelCode.WALLET.name();
        }
        throw new IllegalArgumentException("未知提现方式：" + type);
    }

    @Override
    public PagedResultDto<PayDemoWithdraw> getDemoWithdrawPage(PagedResultRequestDto input) {
        return toPaged(demoTransferRepo.findAll(toPage(input)));
    }

    @Override
    public void updateDemoWithdrawTransferred(Long id, String payTransferNo) {
        // 1.1 校验转账单是否存在
        PayDemoWithdraw withdraw = demoTransferRepo.findById(id).orElseThrow(() -> exception(DEMO_WITHDRAW_NOT_FOUND));
        if (withdraw == null) {
            log.error("[updateDemoWithdrawStatus][withdraw({}) payOrder({}) 不存在提现单，请进行处理！]", id, payTransferNo);
            throw exception(DEMO_WITHDRAW_NOT_FOUND);
        }
        // 1.2 校验转账单已成结束（成功或失败）
        if (PayDemoWithdrawStatus.isSuccess(withdraw.getStatus())
                || PayDemoWithdrawStatus.isClosed(withdraw.getStatus())) {
            // 特殊：转账单编号相同，直接返回，说明重复回调
            if (Objects.equals(withdraw.getPayTransferNo(), payTransferNo)) {
                log.warn("[updateDemoWithdrawStatus][withdraw({}) 已结束，且转账单编号相同({})，直接返回]", withdraw, payTransferNo);
                return;
            }
            // 异常：转账单编号不同，说明转账单编号错误
            log.error("[updateDemoWithdrawStatus][withdraw({}) 转账单不匹配({})，请进行处理！]", withdraw, payTransferNo);
            throw exception(DEMO_WITHDRAW_UPDATE_STATUS_FAIL_PAY_TRANSFER_ID_ERROR);
        }

        // 2. 校验提现单的合法性
        PayTransferDto payTransfer = validateDemoTransferStatusCanUpdate(withdraw, payTransferNo);

        // 3. 更新示例订单状态
        PayDemoWithdrawStatus newStatus = PayTransferStatus.isSuccess(payTransfer.getStatus()) ? PayDemoWithdrawStatus.SUCCESS :
                PayTransferStatus.isClosed(payTransfer.getStatus()) ? PayDemoWithdrawStatus.CLOSED : null;
        Assert.notNull(newStatus, "转账单状态(" + payTransfer.getStatus() + ") 不合法");
        demoTransferRepo.updateByIdAndStatus(
                withdraw.getId(),
                withdraw.getStatus(),
                newStatus,
                payTransfer.getSuccessTime(),
                payTransfer.getChannelErrorMsg()
        );
    }

    private PayTransferDto validateDemoTransferStatusCanUpdate(PayDemoWithdraw withdraw, String payTransferNo) {
        // 1. 校验转账单是否存在
        PayTransferDto payTransfer = payTransferApi.getTransferByNo(payTransferNo);
        if (payTransfer == null) {
            log.error("[validateDemoTransferStatusCanUpdate][withdraw({}) payTransfer({}) 不存在，请进行处理！]", withdraw.getId(), payTransferNo);
            throw exception(PAY_TRANSFER_NOT_FOUND);
        }

        // 2.1 校验转账单已成功
        if (!PayTransferStatus.isSuccessOrClosed(payTransfer.getStatus())) {
            log.error("[validateDemoTransferStatusCanUpdate][withdraw({}) payTransfer({}) 未结束，请进行处理！payTransfer 数据是：{}]",
                    withdraw.getId(), payTransferNo, JsonUtils.toJson(payTransfer));
            throw exception(DEMO_WITHDRAW_UPDATE_STATUS_FAIL_PAY_TRANSFER_STATUS_NOT_SUCCESS_OR_CLOSED);
        }
        // 2.2 校验转账金额一致
        if (ObjectUtils.notEqual(payTransfer.getPrice(), withdraw.getPrice())) {
            log.error("[validateDemoTransferStatusCanUpdate][withdraw({}) payTransfer({}) 转账金额不匹配，请进行处理！withdraw 数据是：{}，payTransfer 数据是：{}]",
                    withdraw.getId(), payTransferNo, JsonUtils.toJson(withdraw), JsonUtils.toJson(payTransfer));
            throw exception(DEMO_WITHDRAW_UPDATE_STATUS_FAIL_PAY_PRICE_NOT_MATCH);
        }
        // 2.3 校验转账订单匹配（二次）
        if (ObjectUtils.notEqual(payTransfer.getMerchantTransferId(), Objects.requireNonNull(withdraw.getId()).toString())) {
            log.error("[validateDemoTransferStatusCanUpdate][withdraw({}) 转账单不匹配({})，请进行处理！payTransfer 数据是：{}]",
                    withdraw.getId(), payTransferNo, JsonUtils.toJson(payTransfer));
            throw exception(DEMO_WITHDRAW_UPDATE_STATUS_FAIL_PAY_MERCHANT_EXISTS);
        }
        // 2.4 校验转账渠道一致
        if (ObjectUtils.notEqual(payTransfer.getChannelCode(), withdraw.getTransferChannelCode())) {
            log.error("[validateDemoTransferStatusCanUpdate][withdraw({}) payTransfer({}) 转账渠道不匹配，请进行处理！withdraw 数据是：{}，payTransfer 数据是：{}]",
                    withdraw.getId(), payTransferNo, JsonUtils.toJson(withdraw), JsonUtils.toJson(payTransfer));
            throw exception(DEMO_WITHDRAW_UPDATE_STATUS_FAIL_PAY_CHANNEL_NOT_MATCH);
        }
        return payTransfer;
    }

}
